/*  extMDB 1.1 - an extension to gvSIG Desktop GIS to read GeoMedia MDB warehouses
 *  Copyright (C) 2008-2009  Stefano Orlando
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
 * Revision 1.1  02/05/09 Stefano Orlando
 * Revision 1.0  06/02/09 Stefano Orlando
 */
package com.iver.cit.gvsig.mdb.panels;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.text.Document;

import org.cresques.cts.IProjection;
import org.gvsig.crs.ICrs;
import org.gvsig.crs.gui.dialog.TRSelectionDialog;

import com.iver.andami.PluginServices;
import com.iver.andami.ui.mdiManager.IWindow;
import com.iver.cit.gvsig.fmap.MapContext;
import com.iver.cit.gvsig.fmap.MapControl;
import com.iver.cit.gvsig.fmap.layers.FLayer;
import com.iver.cit.gvsig.fmap.layers.FLayers;
import com.iver.cit.gvsig.gui.WizardPanel;
import com.iver.cit.gvsig.gui.wizards.WizardListener;
import com.iver.cit.gvsig.gui.wizards.WizardListenerSupport;
import com.iver.cit.gvsig.mdb.core.MDBException;
import com.iver.cit.gvsig.mdb.core.MDBWarehouse;
import com.iver.cit.gvsig.project.documents.view.gui.View;
/**
 * Panel integrated in the "Add Layer" wizard for accessing GeoMedia warehouses.
 * @author Stefano Orlando
 */
public class MDBWizard extends WizardPanel implements DocumentListener {
	private static final long serialVersionUID = 2624907899565017168L;
	private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JPanel jPanel0;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
	private tableModel tbm;
	private listSelectionModel lsm;

	private File fMDBFileName = null;
	private IProjection currentProjection;
	private IProjection featureClassesProj[];
	private MDBWarehouse mdbWarehouse = null;
	private WizardListenerSupport listenerSupport = new WizardListenerSupport();
	private boolean isFirstPaint = true;
	private static final Color COLOR_TABLE = new Color(0xff,0xcc,0xcc);;
    /**
     * Constructor of the wizard panel.
     * @author Stefano Orlando
     */
	public MDBWizard() {
		super();
        IWindow iw = PluginServices.getMDIManager().getActiveWindow();
        if (iw == null) {
            return;
        }
        if (!(iw instanceof View)) {
            return;
        }
		MapControl mapCtrl = ((View)iw).getMapControl();
		currentProjection = mapCtrl.getProjection();
		setTabName("MDB");
		initComponents();
		initActionListeners();
		makeLayout();
	}
	public void execute() {
		// TODO Auto-generated method stub
	}
    /**
     * Returns the selected Feature Classes from the MDB Warehouse.
     * @return a tree of layers (FLayers) representing the imported Feature Classes
     * @author Stefano Orlando
     */
	public FLayer getLayer() {
        MapControl mapControl = null;
        MapContext mapContext = null;
        IWindow w = PluginServices.getMDIManager().getActiveWindow();
        if (w instanceof View) {
	        View v = (View) w;
	        mapControl = v.getMapControl();
	        mapContext = mapControl.getMapContext();
        }
        FLayers fls = new FLayers();
        fls.setMapContext(mapContext);
        fls.setName(mdbWarehouse.getName());
        ArrayList<String> featureClasses = new ArrayList<String>();
        ArrayList<IProjection> featureClassesProjs = new ArrayList<IProjection>();
        for (int i = 0; i < jTable1.getRowCount(); i++) {
        	if(((Boolean)(jTable1.getValueAt(i, 0))).booleanValue() == true) {
        		featureClasses.add(jTable1.getValueAt(i, 1).toString());
        		IProjection proj = null;
        		if (featureClassesProj[i] == null) proj = currentProjection;
        		else proj = featureClassesProj[i];
        		featureClassesProjs.add(proj);
        	}
        }
        MDBProgress process = new MDBProgress(mdbWarehouse, featureClasses, featureClassesProjs, fls);
        process.start();
        PluginServices.getMDIManager().addWindow(process);
        fls = process.getResult();
        if (fls != null) {
        	if (fls.getLayersCount() == 0) fls = null;
        }
		if (mdbWarehouse != null) {
	        try {
				mdbWarehouse.close();
			} catch (MDBException ex) {
				// we're closing, so no error messages...
				ex.printStackTrace();
			}
			mdbWarehouse = null;
		}
        return fls;
	}
	public void initWizard() {
		// TODO Auto-generated method stub		
	}
    /**
     * Initializes the GUI's components of the wizard panel.
     * @author Stefano Orlando
     */
    private void initComponents() {
    	jPanel0 = new javax.swing.JPanel();
        jPanel1 = new javax.swing.JPanel();
        jTextField1 = new javax.swing.JTextField();
        jButton1 = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		jTable1 = new javax.swing.JTable() {
			public Component prepareRenderer(TableCellRenderer renderer, int rowIndex, int colIndex) {
				Component c = super.prepareRenderer(renderer, rowIndex, colIndex);
				if (c instanceof JComponent) {
					if( ((String)tbm.getToolTip(rowIndex) == null) || (((String)(tbm.getToolTip(rowIndex))).length() == 0)) {
						((JComponent)c).setToolTipText(null);
					}
					else {
						((JComponent)c).setToolTipText((String)tbm.getToolTip(rowIndex));	
					}
					if(!isCellSelected(rowIndex, colIndex)) {
						if(!isCellEditable(rowIndex, 0)) c.setBackground(COLOR_TABLE);
						else c.setBackground(null);
					}
				}
				return c;
			}
		};
        jButton2 = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        
        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(
                null, PluginServices.getText(this, "warehouse"),
                javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION,
                javax.swing.border.TitledBorder.DEFAULT_POSITION, null, null));
        jTextField1.setText(null);
        jButton1.setText(PluginServices.getText(this,"search_button"));
        jButton1.setName("search");
        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(PluginServices.getText(this,"feature_classes"))); 
        tbm = new tableModel();
		jTable1.setModel(tbm);
		lsm = new listSelectionModel();
		jTable1.setSelectionModel(lsm);
		jTable1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		jTable1.getTableHeader().setReorderingAllowed(false);
        jScrollPane1.setViewportView(jTable1);
        jLabel1.setText("<html>" + PluginServices.getText(this,"label_coordinate_system") +
				"<br>(" + PluginServices.getText(this,"current_view") + ": " +
				currentProjection.getFullCode() + ")</html>");
        int jLabel1Width = Math.max(
        		(int)Math.round(jLabel1.getFontMetrics(jLabel1.getFont()).getStringBounds(
        				PluginServices.getText(this,"label_coordinate_system") + " ",
        				null).getWidth()),
        		(int)Math.round(jLabel1.getFontMetrics(jLabel1.getFont()).getStringBounds(
        				"(" + PluginServices.getText(this,"current_view") + ": " +
        				currentProjection.getFullCode() + ")",
        				null).getWidth())
        );
        jLabel1.setMinimumSize(new Dimension(jLabel1Width, jLabel1.getMinimumSize().height));
        jLabel1.setPreferredSize(new Dimension(jLabel1Width, jLabel1.getPreferredSize().height));
        jLabel1.setMaximumSize(new Dimension(jLabel1Width, jLabel1.getMaximumSize().height));
        jButton2.setText("  ...  ");
        jLabel2.setText("<html><B>" + PluginServices.getText(this,"note") + ":</B> " +
				PluginServices.getText(this,"coordinate_system_note") + "</html>");
	}
    /**
     * Defines the layout of the wizard panel.
     * @author Stefano Orlando
     */
	private void makeLayout() {
		jPanel1.setLayout(new BoxLayout(jPanel1, BoxLayout.LINE_AXIS));
		jPanel1.setAlignmentX(Component.LEFT_ALIGNMENT);
        jPanel1.add(jTextField1);
        jPanel1.add(Box.createRigidArea(new Dimension(5, 0)));
        jPanel1.add(jButton1);
        jPanel2.setLayout(new BoxLayout(jPanel2, BoxLayout.PAGE_AXIS));
        jPanel2.setAlignmentX(Component.LEFT_ALIGNMENT);
        jScrollPane1.setAlignmentX(Component.LEFT_ALIGNMENT);
        jPanel2.add(jScrollPane1);
        jPanel2.add(Box.createRigidArea(new Dimension(0,10)));
        jPanel3.setLayout(new BoxLayout(jPanel3, BoxLayout.LINE_AXIS));
        jPanel3.add(jLabel1);
        jPanel3.add(Box.createRigidArea(new Dimension(10,0)));
        jPanel3.add(jButton2);
        jPanel3.setAlignmentX(Component.LEFT_ALIGNMENT);
        jPanel2.add(jPanel3);
        jPanel2.add(Box.createRigidArea(new Dimension(0,5)));
		jLabel2.setAlignmentX(Component.LEFT_ALIGNMENT);
        jPanel2.add(jLabel2);
        jPanel0.setLayout(new BoxLayout(jPanel0, BoxLayout.PAGE_AXIS));
        jPanel0.add(jPanel1);
        jPanel0.add(jPanel2);
        GridBagLayout gbl = new GridBagLayout();
		setLayout(gbl);
		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(5,5,5,5);
		c.fill = GridBagConstraints.BOTH;
        add(jPanel0, c);
        // forces the layout of components before calling getLayoutWeights()
        // otherwise the result of getLayoutWeights() is not valid
        gbl.layoutContainer(this);
        // sets weights of all columns and rows to 1
        double[][] weights = gbl.getLayoutWeights();
        for (int i=0; i<2; i++) {
            for (int j=0; j<weights[i].length; j++) {
                weights[i][j] = 1.0d;
            }
        }
        gbl.columnWeights = weights[0];
        gbl.rowWeights = weights[1];
    }
    /**
     * Provides the implementation for the DefaultListSelectionModel used by
     * the jTable that displays the Feature Classes in the wizard panel.
     * @author Stefano Orlando
     */    
    class listSelectionModel extends DefaultListSelectionModel {
    	
		private static final long serialVersionUID = 8077678512967829852L;
		public boolean isSelectedIndex(int index)
        {
    		if ((index < 0) || (tbm.isCellEditable(index, 0) == false)) return false;
    		else return super.isSelectedIndex(index);
        }
    }
    /**
     * Provides the implementation for the TableModel used by the jTable that
     * displays the Feature Classes in the wizard panel.
     * @author Stefano Orlando
     */
	class tableModel extends javax.swing.table.AbstractTableModel {
		private static final long serialVersionUID = 312475880967271590L;
		private Object [][] data = null;
		private Object [] tooltip = null;
        private String [] columnNames = {
        	" "+PluginServices.getText(this,"import_header")+" ",
        	" "+PluginServices.getText(this,"name_header")+" ",
        	" "+PluginServices.getText(this,"geometry_type_header")+" ",
        	" "+PluginServices.getText(this,"coordinate_system_header")+" ",
        };
        Class[] types = new Class [] {
            java.lang.Boolean.class,
            java.lang.String.class,
            java.lang.String.class,
            java.lang.String.class
        };
        public Class getColumnClass(int columnIndex) {
        	return types [columnIndex];
        }
		public int getColumnCount() {
			return columnNames.length;
		}
		public int getRowCount() {
			if (data == null) return 0;
			else return data.length;
		}	
		public String getColumnName(int col) {
			return columnNames[col];
		}	
		public Object getValueAt(int rowIndex, int columnIndex) {
			return data[rowIndex][columnIndex];
		}
		public void addRows(Object[][] newData) {
			clearTable();
			data = new Object[newData.length][getColumnCount()];
			tooltip = new Object[newData.length];
			for (int i = 0; i < newData.length; i++) {
				data[i][0]=new Boolean(false);
				data[i][1]=newData[i][0];
				data[i][2]=newData[i][1];
				if (isCellEditable(i,0) == false) {
					data[i][3]=PluginServices.getText(this,"not_applicable");
					tooltip[i]=PluginServices.getText(this,"unsupported_feature_class");
				}
				else if (data[i][2].toString().equalsIgnoreCase("None") == true) {
					data[i][3]=PluginServices.getText(this,"not_selectable");
					tooltip[i]=newData[i][2];
				}
				else {
					data[i][3]="<"+PluginServices.getText(this,"current_view")+">";
					tooltip[i]=newData[i][2];
				}
			}
		    fireTableDataChanged();
		}
	    public boolean isCellEditable(int row, int col) {
	        // note that the data/cell address is constant,
	        // no matter where the cell appears onscreen
	        if (col < 1) {
	        	// unsupported Feature Classes
	            if (getValueAt(row,2).toString().equalsIgnoreCase("Text") ||
	               (getValueAt(row,2).toString().equalsIgnoreCase("Image")))
	            	return false;
	            else return true;
	        } else {
	            return false;
	        }
	    }
	    public void setValueAt(Object value, int row, int col) {
	    	data[row][col] = value;
	        fireTableCellUpdated(row, col);
			if (isSelection() == true) listenerSupport.callStateChanged(true);
			else listenerSupport.callStateChanged(false);	        
	    }
	    public void clearTable()
	    {
	    	data = null;
	    	tooltip = null;
	    	fireTableDataChanged();
	    }
	    public Object getToolTip(int row) {
	    	return tooltip[row];
	    }
		public boolean isSelection() {
			int rowCount = getRowCount();
			for (int i = 0; i < rowCount; i++) {
				if (Boolean.TRUE.equals(data[i][0])) {
					return true;
				}
			}
			return false;
		}
	}
	/**
	 * Adds the listeners for the jButtons and the jTextField on the wizard panel.
	 * @author Stefano Orlando
	 */
	private void initActionListeners () {
		// jButton1
		jButton1.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				openButtonActionPerformed(evt);
        		}
    		}
		);
		// jButton2
		jButton2.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				prjButtonActionPerformed(evt);
        		}
    		}
		);
		// jTextField1
		Document document =	jTextField1.getDocument();
		document.addDocumentListener(this);
	}
	/**
	 * Processes the action on the open/search button (jButton1).
	 * @param e - event
	 * @author Stefano Orlando
	 */
	private void openButtonActionPerformed(ActionEvent e) {
		// jButton1 --> name --> search
		if(jButton1.getName().equalsIgnoreCase("search") == true) {
			JFileChooser chooser = new JFileChooser(fMDBFileName);
			chooser.setDialogTitle(PluginServices.getText(this,"open_button"));
            chooser.addChoosableFileFilter(new FileFilter() {
                public boolean accept(File f) {
                    if (f.isDirectory()) {
                        return true;
                    }
                    String s = f.getName();
                    int i = s.lastIndexOf('.');
                    if (i > 0 && i < s.length() - 1)
                    	if (s.substring(i+1).toLowerCase().equals("mdb"))
                    		return true;
                    return false;
                }
                public String getDescription() {
                    return PluginServices.getText(this,"mdb_file");
                }
            });
			int returnVal = chooser.showOpenDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				jTextField1.setText(chooser.getSelectedFile().toString());
				jButton1.setText(PluginServices.getText(this,"search_button"));
				jButton1.setName("search");
				jTextField1.requestFocusInWindow();
				jTextField1.selectAll();			
				fMDBFileName = chooser.getSelectedFile();
			}
			else {
				chooser = null;
				return;
			}
		}
		// jButton1 --> name --> open
		else {
			fMDBFileName = new File(jTextField1.getText());
			if(fMDBFileName.isFile()) {
				jTextField1.setText(fMDBFileName.toString());
			}
			else {
				JOptionPane.showMessageDialog(
					null,
					PluginServices.getText(this,"mdb_notfound_invalid_error"),
					PluginServices.getText(this,"error_message"),
					JOptionPane.ERROR_MESSAGE);
				jButton1.setText(PluginServices.getText(this,"search_button"));
				jButton1.setName("search");
				jTextField1.requestFocusInWindow();
				jTextField1.selectAll();				
				return;
			}
		}
		try {
			if (mdbWarehouse != null) {
				mdbWarehouse.close();
				mdbWarehouse = null;
			}
			tbm.clearTable();
			mdbWarehouse = new MDBWarehouse(fMDBFileName);
			String[][] featureClassTable = mdbWarehouse.getFeatureClassTable();
			featureClassesProj = null;
			if (featureClassTable != null) {
				tbm.addRows(featureClassTable);
				featureClassesProj = new IProjection[jTable1.getRowCount()];
			}
			else {
				JOptionPane.showMessageDialog(
					null,
					PluginServices.getText(this,"wh_empty_warning"),
					PluginServices.getText(this,"warning_message"),
					JOptionPane.INFORMATION_MESSAGE);
			}
			jButton1.setText(PluginServices.getText(this,"search_button"));
			jButton1.setName("search");
			jTextField1.requestFocusInWindow();
			jTextField1.selectAll();	
		}
		catch (MDBException ex){
			// if this exception is thrown with the message: "aborted_MDBtoHSQLDB",
			// then there's not an error: the conversion MS Access --> HSQLDB has
			// been aborted by the user
			if (!ex.getMessage().equalsIgnoreCase("aborted_MDBtoHSQLDB"))
				JOptionPane.showMessageDialog(
					null,
					ex.getMessage(),
					PluginServices.getText(this,"error_message"),
					JOptionPane.ERROR_MESSAGE);
			jButton1.setText(PluginServices.getText(this,"search_button"));
			jButton1.setName("search");
			jTextField1.requestFocusInWindow();
			jTextField1.selectAll();
			return;
		}
	}
	/**
	 * Processes the action on the projection button (jButton2).
	 * @param e - event
	 * @author Stefano Orlando
	 */	
	private void prjButtonActionPerformed(ActionEvent e) {
		if((jTable1.getRowCount() == 0) || (jTable1.getSelectedRowCount() == 0)) {
			JOptionPane.showMessageDialog(
				null,
				PluginServices.getText(this,"no_selection_error"),
				PluginServices.getText(this,"error_message"),
				JOptionPane.ERROR_MESSAGE);
			return;
		}
		boolean isSelectable = false;
		for (int i = 0; i < jTable1.getSelectedRowCount(); i++) {
			if (jTable1.getValueAt(jTable1.getSelectedRows()[i],2)
				.toString().equalsIgnoreCase("None") == false) isSelectable = true;
		}
		if (isSelectable == false) {
			JOptionPane.showMessageDialog(
				null,
				PluginServices.getText(this,"selection_not_allowed_error"),
				PluginServices.getText(this,"error_message"),
				JOptionPane.ERROR_MESSAGE);
			return;
		}
		IProjection curProj = currentProjection;
        TRSelectionDialog trSelect = new TRSelectionDialog((ICrs)curProj);
        trSelect.setProjection(curProj);
		trSelect.setLayout(new GridLayout(0,1));
        PluginServices.getMDIManager().addWindow(trSelect);
        IProjection newProj = trSelect.getProjection();
        if(newProj==null) return;
        String abrev = newProj.getAbrev();
       	for (int i = 0; i < jTable1.getSelectedRowCount(); i++) {
       		if (jTable1.getValueAt(jTable1.getSelectedRows()[i],2)
    				.toString().equalsIgnoreCase("None") == true) continue;
       		jTable1.setValueAt(abrev, jTable1.getSelectedRows()[i], 3);
       		featureClassesProj[jTable1.getSelectedRows()[i]] = newProj;
       	}
        return;
	}
	/**
	 * Provides support to the GUI managing the 'Ok' button of the wizard and
	 * the columns' widths of the table that shows the Feature Classes.
	 * @author Stefano Orlando
	 */
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		if (tbm.isSelection() == true) listenerSupport.callStateChanged(true);
		else listenerSupport.callStateChanged(false);
		if (isFirstPaint  == true) {
			isFirstPaint = false;
			// sets the columns' width of the jTable (avoiding the second column)
			// considering the headers' widths
			TableCellRenderer tcr = jTable1.getTableHeader().getDefaultRenderer();
			int totalColWidth = 0;
			for (int i = 0; i < jTable1.getColumnCount(); i+=1) {
				if(i==1) continue;			
				TableColumn col = jTable1.getColumnModel().getColumn(i);
				Component c = tcr.getTableCellRendererComponent(jTable1,col.getHeaderValue(),false,false,-1,-1);
				col.setPreferredWidth(c.getPreferredSize().width);
				col.setWidth(col.getPreferredWidth());
				totalColWidth += c.getPreferredSize().width;
			}
			// sets the width of second column filling the remaining space of the
			// jScrollPane that contains the jTable
			jTable1.getColumnModel().getColumn(1).setPreferredWidth(jScrollPane1.getViewportBorderBounds().width - totalColWidth);
			jTable1.getColumnModel().getColumn(1).setWidth(jScrollPane1.getViewportBorderBounds().width - totalColWidth);
		}
	}
	public void changedUpdate(DocumentEvent e) {
		// TODO Auto-generated method stub	
	}
    /**
     * Processes the insertion of text in the textbox (jTextBox1).
     * @param e - event
     * @author Stefano Orlando
     */
	public void insertUpdate(DocumentEvent e) {
		jButton1.setText(PluginServices.getText(this,"open_button"));
		jButton1.setName("open");
	}
    /**
     * Processes the removal of text in the textbox (jTextBox1).
     * @param e - event
     * @author Stefano Orlando
     */
	public void removeUpdate(DocumentEvent e) {

		if ((jTextField1.getText().equals(null) == true) ||
			(jTextField1.getText().length() == 0)) {
			jButton1.setText(PluginServices.getText(this,"search_button"));
			jButton1.setName("search");	
		}
		else {
			jButton1.setText(PluginServices.getText(this,"open_button"));
			jButton1.setName("open");
		}
	}
	/**
	 * Adds the gvSIG's wizard listener.
	 * @param listener - listener to add
	 * @author Stefano Orlando
	 */
	public void addWizardListener(WizardListener listener) {
		listenerSupport.addWizardListener(listener);
	}
	/**
	 * Removes the gvSIG's wizard listener.
	 * @param listener - listener to remove
	 * @author Stefano Orlando
	 */
	public void removeWizardListener(WizardListener listener) {
		listenerSupport.removeWizardListener(listener);
	}
}
